package com.kt.thread.demo;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Created by tangwy on 2016/1/19.
 * 使用Condition实现子线程循环10次，接着主线程循环100次，接着又回到子线程循环10次，接着再回到主线程循环100次，
 * 其实Condition也是和wait()和notify是一样的效果
 */
public class ConditionTest {
    Lock lock = new ReentrantLock();
    Condition condition = lock.newCondition();
    boolean flag = false;

    public void sub() {
        //使用lock实现互斥,而不是synchronized
        lock.lock();
        try {
            while (flag) {
                try {
                    //如果为true，则等待，让其此线程等待
                    condition.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            for (int i = 1; i <= 10; i++) {
                System.out.println("子线程执行,i=" + i);
            }
            //通知其他线程可以执行,并改变flag
            flag = !flag;
            condition.signal();
        } finally {
            lock.unlock();
        }
    }

    public void main() {
        lock.lock();
        try {
            while (!flag) {
                try {
                    condition.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            for (int i = 1; i <= 100; i++) {
                System.out.println("主线程执行,i=" + i);
            }
            //通知其他线程可以执行,并改变flag
            flag = !flag;
            condition.signal();
        } finally {
            lock.unlock();
        }
    }

    public void conditionTest() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    sub();
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
        while (true) {
            main();
        }
    }

    Object[] objects = new Object[10];
    int puter, count, taker;
    Condition full = lock.newCondition();
    Condition empty = lock.newCondition();

    public void puter(Object x) {
        lock.lock();
        try {

            //如果数组满了，则等待取出
            //while (count == objects.length) {
            while (count>0){
                try {
                    full.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("放入" + x);
            //放入数据
            objects[puter] = x;
            //如果到了最后一个位置，则puter又重0开始
            if (++puter == objects.length) {
                puter = 0;
            }
            //当前放了多少个加1
            ++count;
            //唤醒取的线程，取走数据
            empty.signal();
        } finally {
            lock.unlock();
        }
    }

    public Object take() {
        lock.lock();
        try {
            //如果一个都没有则等待
            while (count == 0) {
                try {
                    empty.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            Object x = objects[taker];
            //如果取到最后一个，则从0开始取
            if (++taker == objects.length) {
                taker = 0;
            }
            //数量减1
            --count;
            System.out.println("取出" + x);
            //通知放的线程执行
            full.signal();
            return x;
        } finally {
            lock.unlock();
        }
    }

    BoundedBuffer buffer = new BoundedBuffer();

    public void testPuterAndTake() {
        for (int j = 0; j < 2; j++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 1; i <= 20; i++) {
                        puter(i);
                    }
                }
            }).start();

            new Thread(new Runnable() {
                @Override
                public void run() {
                    while (true) {
                        take();
                    }
                }
            }).start();
            new Thread(new Runnable() {
                @Override
                public void run() {
                    while (true) {
                        take();
                    }
                }
            }).start();
        }
    }
}
